package com.frontalcode
{
	import flash.events.*;
	
	/**
	 * The FrontalObject is the base class of most of our objects. It sends the 
	 * standard construct and destroy events.
	 *		
	 * The base class also helps us manage our event listeners. If we 
	 * destroy ourselves then we remove all of our listeners. Also, when we add 
	 * a listener to a FrontalObject then we also add a listener to its destroy 
	 * event so that we can remove listeners then too.
	 */
	public class FrontalObject extends EventDispatcher
	{
		private var eventListeners : Object = { };
		
		/**
		 * Creates a new FrontalObject instance.
		 */
		public function FrontalObject ( )
		{
			super ( );
			
			dispatchEvent ( new FrontalEvent ( FrontalEvent.CONSTRUCT ) );
		}
		
		public function destroy ( ) : void
		{
			dispatchEvent ( new FrontalEvent ( FrontalEvent.DESTROY ) );
		}

		/*
			Function: addListener
			
				FrontalObject's use this method to add a listener so that we
				can register it. This is so that when we destroy ourselves, we
				can remove our listeners. We also listen for destroy events from 
				our dispatchers so we can remove listeners then as well.
				
				Note that we change the default value of useWeakReference to 
				true for our event listeners to better aid the garbage 
				collection of event listeners.
		*/
		public function addListener ( dispatcher : EventDispatcher, type : String, listener : Function, useCapture : Boolean = false, priority : int = 0, useWeakReference : Boolean = true ) : void
		{
			if ( dispatcher == null ) return;
			var index : int = findListener ( dispatcher, type, listener, useCapture );
			if ( index < 0 )
			{
				dispatcher.addEventListener ( type, listener, useCapture, priority, useWeakReference );
				if ( dispatcher is FrontalObject ) dispatcher.addEventListener ( FrontalEvent.DESTROY, removeListenersOnDestroyHandler, false, -1000, true );
							                    
				if ( eventListeners [ type ] == null ) eventListeners [ type ] = [ ];
				eventListeners [ type ].push ( {
						dispatcher : dispatcher,
						type : type,
						listener : listener,
						useCapture : useCapture
					} );
			}
		}
		
		/**
		 * @private
		 */
		internal function removeListenersOnDestroyHandler ( event : FrontalEvent ) : void
		{
			for ( var type : String in eventListeners )
			{
				for ( var i : int = 0; i < eventListeners [ type ].length; i++ )
				{
					var entry : Object = eventListeners [ type ] [ i ];
					
					if ( entry.dispatcher === event.target )
					{
						entry.dispatcher.removeEventListener ( entry.type, entry.listener, entry.useCapture );
						eventListeners [ type ].splice ( i, 1 );
						i--;
					}
				}
			}
		}
		
		private function findListener ( dispatcher : EventDispatcher, type : String, listener : Function, useCapture : Boolean = false ) : int
		{
			if ( eventListeners [ type ] != null )
			{
				for ( var i : int = 0; i < eventListeners [ type ].length; i++ )
				{
					var entry : Object = eventListeners [ type ] [ i ];
					if ( entry.dispatcher === dispatcher && entry.type == type && entry.listener === listener && entry.useCapture == useCapture ) return i;
				}
			}
			return -1;
		}
		
		public function removeListener ( dispatcher : EventDispatcher, type : String, listener : Function, useCapture : Boolean = false ) : void
		{
			if ( dispatcher == null ) return;
			dispatcher.removeEventListener ( type, listener, useCapture );
			
			var index : int = findListener ( dispatcher, type, listener, useCapture );
			if ( index >= 0 ) eventListeners [ type ].splice ( index, 1 );
		}
	}
}	
